package com.doudou.collection.utils;

import java.util.concurrent.ThreadLocalRandom;

/**
 * @ClassName SnowflakeIdGenerator
 * @Description 唯一ID生成器
 * @Author blue sky
 * @Date 2023/6/8 22:59
 * @Version 1.0
 */

public class SnowflakeIdGenerator {
    private static final long START_TIMESTAMP = 1623158400000L; // 2021-06-09 00:00:00
    private static final long MAX_WORKER_ID = 1023L;
    private static final long SEQUENCE_BITS = 12L;
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long TIMESTAMP_SHIFT = WORKER_ID_SHIFT + 10L;
    private static final long SEQUENCE_MASK = (1L << SEQUENCE_BITS) - 1L;

    private static long lastTimestamp = -1L;
    private static long sequence = ThreadLocalRandom.current().nextLong(1L, 4L);

    private static long workerId = ThreadLocalRandom.current().nextLong(0L, MAX_WORKER_ID + 1);

    public static synchronized long generateUniqueId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = ThreadLocalRandom.current().nextLong(1L, 4L);
        }

        lastTimestamp = timestamp;

        return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence;
    }

    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}
